/*
 * Copyright 2012 Gregor Schauer
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package at.schauer.gregor.vienna;

import at.schauer.gregor.vienna.context.DefaultViennaContext;
import at.schauer.gregor.vienna.context.ViennaContextManager;
import at.schauer.gregor.vienna.convert.provider.ConverterProvider;
import at.schauer.gregor.vienna.convert.vienna.SimpleConverter;
import at.schauer.gregor.vienna.convert.vienna.ViennaConverterProvider;
import at.schauer.gregor.vienna.datastore.DataStore;
import at.schauer.gregor.vienna.datastore.MemoryDataStore;
import at.schauer.gregor.vienna.identifier.IdentifierGenerator;
import at.schauer.gregor.vienna.identifier.IncrementGenerator;
import at.schauer.gregor.vienna.metadata.MetadataProvider;
import at.schauer.gregor.vienna.metadata.SimpleObjectMetadataProvider;
import at.schauer.gregor.vienna.serializer.DefaultSerializer;
import at.schauer.gregor.vienna.serializer.DefaultSerializerIT;
import at.schauer.gregor.vienna.serializer.Serializer;
import at.schauer.gregor.vienna.serializer.builder.DefaultConstructorBuilder;
import at.schauer.gregor.vienna.serializer.builder.ObjectBuilder;
import at.schauer.gregor.vienna.serializer.decomposer.DirectFieldDecomposer;
import at.schauer.gregor.vienna.serializer.decomposer.ObjectDecomposer;
import at.schauer.gregor.vienna.serializer.plugin.DefaultSerializerPlugin;
import at.schauer.gregor.vienna.serializer.plugin.SerializerPlugin;
import at.schauer.gregor.vienna.serializer.plugin.ToStringSerializerPlugin;
import at.schauer.gregor.vienna.serializer.populator.DirectFieldPopulator;
import at.schauer.gregor.vienna.serializer.populator.ObjectPopulator;
import at.schauer.gregor.vienna.test.MathService;
import at.schauer.gregor.vienna.test.SimpleObject;
import org.junit.BeforeClass;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Member;
import java.util.*;

/**
 * @author Gregor Schauer
 */
public abstract class AbstractViennaIT {
	@BeforeClass
	public static void beforeClass() {
		DefaultViennaContext ctx = new DefaultViennaContext();
		ViennaContextManager.getInstance().setContext(ctx);

		Map<Class<?>, MetadataProvider<?>> metadataProviderMap = new HashMap<Class<?>, MetadataProvider<?>>();
		metadataProviderMap.put(MathService.class, new FallbackMetadataProvider());
		metadataProviderMap.put(SimpleObject.class, new SimpleObjectMetadataProvider());

		ToStringSerializerPlugin<Object> toStringSerializerPlugin = new ToStringSerializerPlugin<Object>();
		toStringSerializerPlugin.setViennaContext(ctx);
		List<SerializerPlugin> plugins = DefaultSerializerIT.<SerializerPlugin>singletonList(new DefaultSerializerPlugin<Object>() {
			@Override
			public Map<String, Object> postDeconstruct(Map<String, Object> map, Object obj) {
				map.put("class", obj.getClass());
				return map;
			}
		}, toStringSerializerPlugin);
		Map<Class<?>, List<SerializerPlugin>> pluginMap = DefaultSerializerIT.<Class<?>, List<SerializerPlugin>>singletonMap(Object.class, plugins);

		ctx.setConverterProviderMap(AbstractViennaIT.<Class<?>, ConverterProvider<?>>map(Object.class, new ViennaConverterProvider<SimpleConverter>()));
		ctx.setDataStoreMap(AbstractViennaIT.<Class<?>, DataStore<?>>map(Object.class, new MemoryDataStore<Object>()));
		ctx.setIdentifierGeneratorMap(AbstractViennaIT.<Class<?>, IdentifierGenerator<?, ?>>map(Object.class, new IncrementGenerator<Object>()));
		ctx.setMetadataProviderMap(metadataProviderMap);

		DefaultSerializer<Object> serializer = new DefaultSerializer<Object>();
		serializer.setBuilderMap(AbstractViennaIT.<Class<?>, ObjectBuilder<?>>map(Object.class, new DefaultConstructorBuilder<Object>()));
		serializer.setDecomposerMap(AbstractViennaIT.<Class<?>, ObjectDecomposer<?>>map(Object.class, new DirectFieldDecomposer<Object>()));
		serializer.setPluginMap(pluginMap);
		serializer.setPopulatorMap(AbstractViennaIT.<Class<?>, ObjectPopulator<?>>map(Object.class, new DirectFieldPopulator<Object>()));
		ctx.setSerializerMap(AbstractViennaIT.<Class<?>, Serializer<?>>map(Object.class, serializer));
	}

	public static <T> List<T> list(T... elem) {
		List<T> list = new ArrayList<T>();
		Collections.addAll(list, elem);
		return list;
	}

	public static <K, V> Map<K, V> map(K key, V value) {
		Map<K, V> map = new LinkedHashMap<K, V>();
		map.put(key, value);
		return map;
	}

	@Deprecated
	public static class FallbackMetadataProvider implements MetadataProvider<Object> {
		@Override
		public Object getIdentifier(@Nonnull Object obj) {
			return obj.getClass().getSimpleName();
		}

		@Override
		public void setIdentifier(@Nonnull Object obj, @Nullable Object identifier) {
		}

		@Override
		public String getIdentifierName(@Nonnull Class<?> clazz) {
			return Serializer.CLASS;
		}

		@Override
		public boolean isTransientType(@Nonnull Class<?> clazz) {
			return false;
		}

		@Override
		public <M extends AccessibleObject & Member> boolean isTransient(@Nonnull M member) {
			return false;
		}

		@Nonnull
		@Override
		public String getEntityName(@Nonnull Class<?> clazz) {
			return clazz.getName();
		}
	}
}
